#include "stdafx.h"
#include "TodayHost.h"
#include <algorithm>


const int topImageOffset = 25;

void TodayHost::LoadPlugin(TODAYLISTITEM item)
{
	item.hinstDLL =  LoadLibrary(item.szDLLPath);

	if (!item.hinstDLL)
		return;

	PFNCUSTOMINITIALIZEITEM InitializeCustomItem = (PFNCUSTOMINITIALIZEITEM)GetProcAddress(item.hinstDLL, (LPWSTR )ORDINAL_INITIALIZEITEM);
	if (!InitializeCustomItem)
		return;

	item.hwndCustom = InitializeCustomItem(&item, _hostWindow);
	if (!item.hwndCustom)
		return;

	BOOL result = SendMessage(item.hwndCustom, WM_TODAYCUSTOM_QUERYREFRESHCACHE, (WPARAM)&item, 0);

	SetWindowPos(item.hwndCustom, HWND_TOP, 0, _heightUsed, _windowSize.right, item.cyp, 0);
	_heightUsed += item.cyp;

	_loadedPlugins.push_back(item);
	UpdateScrollPos();
}


void TodayHost::SetBackground(TCHAR* path)
{
	EnterCriticalSection(&_backgroundSync);
	_backgroundBitmap = SHLoadImageFile(path);
	LeaveCriticalSection(&_backgroundSync);
}

void TodayHost::NotifyLostSelection(HWND exceptWnd)
{
	vector<TODAYLISTITEM>::iterator i;
	for (i = _loadedPlugins.begin(); i!= _loadedPlugins.end(); i++)
	{
		if (i->hwndCustom == exceptWnd)
			continue;

		BOOL result = SendMessage(i->hwndCustom, WM_TODAYCUSTOM_LOSTSELECTION, 0, 0);
		InvalidateRect(i->hwndCustom, 0, 1);
	}
}

void TodayHost::UnloadAllPlugins()
{
	vector<TODAYLISTITEM>::iterator i;
	for (i = _loadedPlugins.begin(); i!= _loadedPlugins.end(); i++)
	{
		FreeLibrary(i->hinstDLL);
		DestroyWindow(i->hwndCustom);
	}

	_heightUsed = 0;
	_selectedItem = -1;
	_loadedPlugins.clear();
	UpdateScrollPos();
}


void TodayHost::UpdateScrollPos()
{
	GetClientRect(_hostWindow, &_windowSize);
	SCROLLINFO si;
	ZeroMemory(&si, sizeof(si));
	si.cbSize = sizeof(si);
	si.fMask = SIF_PAGE | SIF_RANGE;
	si.nMin = 0;
	si.nMax = _heightUsed;
	si.nPage =  _windowSize.bottom;
	SetScrollInfo(_hostWindow, SB_VERT, &si, true);

	SCROLLINFO si2;
	ZeroMemory(&si2, sizeof(si2));
	si2.cbSize = sizeof(SCROLLINFO);
	si2.fMask = SIF_POS | SIF_TRACKPOS;
	GetScrollInfo(_hostWindow, SB_VERT, &si2);
	_scrollPos = si2.nPos;
	UpdateTodayPlugins(true);
}

void TodayHost::UpdateTodayPlugins(bool reorder)
{
	GetClientRect(_hostWindow, &_windowSize);
	vector<TODAYLISTITEM>::iterator i;
	
	DWORD heigthUsed = 0;
	bool reorderPlugins = reorder;
	for (i = _loadedPlugins.begin(); i!= _loadedPlugins.end(); i++)
	{
		DWORD prevHeigth = i->cyp;
		BOOL result = SendMessage(i->hwndCustom, WM_TODAYCUSTOM_QUERYREFRESHCACHE, (WPARAM)&(*i), 0);
		if (result)
		{
			if (prevHeigth != i->cyp)
			{
				reorderPlugins = true;
			}
		}
		if (reorderPlugins)
		{
			SetWindowPos(i->hwndCustom, HWND_TOP, 0, heigthUsed - _scrollPos, _windowSize.right, i->cyp, 0);
		}
		if (result)
		{
			InvalidateRect(i->hwndCustom, 0, 1);
		}
		heigthUsed += i->cyp;
	}

	if (_heightUsed != heigthUsed)
	{
		_heightUsed = heigthUsed;
		UpdateScrollPos();
	}
}

bool TodayHost::HasSelectedPlugin()
{
	return _selectedItem >= 0 && _selectedItem < _loadedPlugins.size();
}

int TodayHost::ProcessNavigationMessages(WPARAM wParam)
{
	if (HasSelectedPlugin())
	{
		bool result = SendMessage(_loadedPlugins[_selectedItem].hwndCustom, WM_TODAYCUSTOM_USERNAVIGATION, wParam, 0);

		if (result)
			return 0;

		SendMessage(_loadedPlugins[_selectedItem].hwndCustom, WM_TODAYCUSTOM_LOSTSELECTION, 0, 0);
		InvalidateRect(_loadedPlugins[_selectedItem].hwndCustom, 0, 1);
	}

	bool found = false;
	if (wParam == VK_DOWN)
	{
		for (int i = _selectedItem + 1; i < _loadedPlugins.size(); i++)
		{
			DWORD selectability = _loadedPlugins[i].dwSelectability;
			if (_loadedPlugins[i].dwSelectability >= TODAY_SELECTABILITY_SELECTABLE)
			{
				_selectedItem = i;
				found = true;
				SendMessage(_loadedPlugins[i].hwndCustom, WM_TODAYCUSTOM_RECEIVEDSELECTION, wParam, 0);
				InvalidateRect(_loadedPlugins[_selectedItem].hwndCustom, 0, 1);
				break;
			}
		}
		if (!found)
		{
			_selectedItem = _loadedPlugins.size();
		}
	}
	else if (wParam == VK_UP)
	{
		for (int i = _selectedItem - 1; i >= 0; i--)
		{
			if (_loadedPlugins[i].dwSelectability >= TODAY_SELECTABILITY_SELECTABLE)
			{
				_selectedItem = i;
				found = true;
				SendMessage(_loadedPlugins[i].hwndCustom, WM_TODAYCUSTOM_RECEIVEDSELECTION, wParam, 0);
				InvalidateRect(_loadedPlugins[_selectedItem].hwndCustom, 0, 1);
				break;
			}
		}
		if (!found)
		{
			_selectedItem = -1;
		}
	}
	return 0;
}

void TodayHost::DrawTodayBackground(HWND hWnd)
{
	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(hWnd, &ps);

	HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0));
	HGDIOBJ prevBrush = SelectObject(hdc, brush);

	RECT clientRect;
	GetWindowRect(hWnd, &clientRect);

	Rectangle(hdc, 0, 0, clientRect.right, clientRect.bottom);
	SelectObject(hdc, prevBrush);
	DeleteObject(brush);

	if (_backgroundBitmap != NULL)
	{
		BITMAP bm;
		HDC hdcMem = CreateCompatibleDC(hdc);
		HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, _backgroundBitmap);

		BitBlt(hdc, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, hdcMem, clientRect.left, clientRect.top + topImageOffset, SRCCOPY);

		SelectObject(hdcMem, hbmOld);
		DeleteDC(hdcMem);
	}
	
	EndPaint(hWnd, &ps);
}

LRESULT TodayHost::MessageHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	SCROLLINFO si;

	switch (message)
	{
	case WM_TIMER:
		if (wParam == TODAY_REFRESH_TIMER)
			UpdateTodayPlugins();
		break;

	case TODAYM_GETCOLOR:
		switch (wParam)
		{
		case TODAYCOLOR_HIGHLIGHT:
			return RGB(75, 75, 75);
		case TODAYCOLOR_TEXT:
			return RGB(220, 220, 225);	
		case TODAYCOLOR_HIGHLIGHTEDTEXT:
			return RGB(255, 255, 255);
		default:
			break;
		}
		break;

	case WM_VSCROLL:
		switch (LOWORD(wParam))
		{
		case SB_BOTTOM: 
			_scrollPos = _heightUsed;
			break;
		case SB_LINEDOWN: 
			_scrollPos += LineHeight;
			break;
		case SB_LINEUP:
			_scrollPos -= LineHeight;
			break;
		case SB_PAGEDOWN:
			_scrollPos += _windowSize.bottom;
			break;
		case SB_PAGEUP: 
			_scrollPos -= _windowSize.bottom;
			break;
		case SB_THUMBPOSITION:
			_scrollPos = HIWORD(wParam);
			break;
		case SB_TOP:
			_scrollPos = 0;
			break;
		}

		if (_scrollPos > _heightUsed - _windowSize.bottom)
			_scrollPos = _heightUsed - _windowSize.bottom;

		if (_scrollPos < 0)
			_scrollPos = 0;

		ZeroMemory(&si, sizeof(si));
		si.cbSize = sizeof(si);
		si.fMask = SIF_POS;
		si.nPos = _scrollPos;
				
		UpdateTodayPlugins(true);
		SetScrollInfo(_hostWindow, SB_VERT, &si, true);
		break;

	case TODAYM_DRAWWATERMARK:
		{
			TODAYDRAWWATERMARKINFO* info = (TODAYDRAWWATERMARKINFO*)lParam;

			HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0));
			HGDIOBJ prevBrush = SelectObject(info->hdc, brush);
			Rectangle(info->hdc, info->rc.left, info->rc.top, info->rc.right, info->rc.bottom);
			SelectObject(info->hdc, prevBrush);
			DeleteObject(brush);
		
			if (_backgroundBitmap != NULL)
			{
				RECT rect;
				GetWindowRect(info->hwnd, &rect);

				HDC hdcMem = CreateCompatibleDC(info->hdc);
				HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, _backgroundBitmap);
			

				BitBlt(info->hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hdcMem, rect.left, rect.top + topImageOffset, SRCCOPY);

				SelectObject(hdcMem, hbmOld);
				DeleteDC(hdcMem);
			}
			return 1;
		}
		break;

	case TODAYM_TOOKSELECTION:
		{
			NotifyLostSelection((HWND)wParam);
			UpdateWindow((HWND)wParam);

			vector<TODAYLISTITEM>::iterator i;
			int index = 0;
			for (i = _loadedPlugins.begin(); i != _loadedPlugins.end(); i++)
			{
				if (i->hwndCustom == (HWND)wParam)
				{
					_selectedItem = index;
					break;
				}
				index++;
			}
		}
		break;

	case WM_KEYDOWN:
		switch (wParam)
		{
		case VK_UP: 
		case VK_DOWN:
		case VK_LEFT:
		case VK_RIGHT:
			return ProcessNavigationMessages(wParam);
		case VK_RETURN:
			if (HasSelectedPlugin())
			{
				SendMessage(_loadedPlugins[_selectedItem].hwndCustom, WM_TODAYCUSTOM_ACTION, wParam, 0);
			}
			return 0;
		}
		break;


	case WM_PAINT:
		EnterCriticalSection(&_backgroundSync);
		DrawTodayBackground(hWnd);
		LeaveCriticalSection(&_backgroundSync);
		break;

	case WM_ACTIVATE:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
		_selectedItem = -1;
		NotifyLostSelection(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}